<template>
  <component :is="dialogType"
             v-model="dialogVisible"
             :class="{'hidden-border' : hiddenBorder, 'hidden-horizontal': hiddenHorizontal}"
             :close-on-click-modal="false"
             :close-on-press-escape="false"
             :fullscreen="fullscreen"
             :show-close="false"
             :size="width"
             :width="width"
             destroy-on-close
             draggable
             lock-scroll
             v-bind="getBindValue"
             @close="handleClose"
             @closed="handleClosed"
             @open="handleOpen"
             @opened="handleOpened">
    <template #header="{ close }">
      <div class="header">
        <slot name="title">
          {{ title }}
        </slot>

        <div class="operate">
          <SvgIcon v-if="showFullscreen && !props.drawer"
                   :icon="fullscreen ? 'fullscreen-exit' : 'fullscreen'"
                   size="18"
                   style="margin: 5px"
                   @click="toggleFull"/>
          <SvgIcon icon="ep:close"
                   size="18"
                   style="margin: 5px"
                   @click="close"/>
        </div>
      </div>
    </template>

    <el-scrollbar :max-height="maxHeight" :style="dialogStyle">
      <slot></slot>
    </el-scrollbar>

    <template v-if="slots.footer" #footer>
      <slot name="footer"></slot>
    </template>
  </component>
</template>

<script lang="ts" setup>
import { computed, nextTick, ref, unref, useAttrs, useSlots, watch } from 'vue';
import { isNumber } from '@/utils/is.ts';
import { SvgIcon } from '@/components/icon';
import bus, { KEY_RESIZE } from '@/utils/bus.ts';

const slots = useSlots();

defineOptions({name: 'EcDialog'});

const props = defineProps({
  title: String,
  bodyHeight: {
    type: [String, Number],
    default: '420px',
  },
  autoHeight: {
    type: Boolean,
    default: false,
  },
  showFullscreen: {
    type: Boolean,
    default: true,
  },
  hiddenBorder: {
    type: Boolean,
    default: false,
  },
  hiddenHorizontal: {
    type: Boolean,
    default: true,
  },
  top: {
    type: String,
  },
  width: {
    type: [String, Number],
  },
  drawer: {
    type: Boolean,
    default: false,
  }
});

const model = defineModel<Boolean>();
const dialogVisible = computed({
  get: () => model.value,
  set: (val) => {
    model.value = val;
  },
});

const dialogType = computed(() => {
  return props.drawer ? 'elDrawer' : 'elDialog';
});

const getBindValue = computed(() => {
  const delArr: string[] = ['showFullscreen', 'title', 'maxHeight', 'modelValue', 'autoHeight', 'hiddenBorder', 'hiddenHorizontal', 'width']
  const attrs = useAttrs()
  const obj = {...attrs, ...props}
  for (const key in obj) {
    if (delArr.indexOf(key) !== -1) {
      delete obj[key]
    }
  }
  return obj
});

const fullscreen = ref(false);
const toggleFull = () => fullscreen.value = !unref(fullscreen);
const dialogHeight = ref(isNumber(props.bodyHeight) ? `${props.bodyHeight}px` : props.bodyHeight);

const onEmitResize = () => {
  // 只有全部才计算
  if (fullscreen.value) {
    computeDialogHeight();
  }
};

/** 回调事件 */
const emit = defineEmits(['open', 'opened', 'close', 'closed']);

/** Dialog 打开的回调 */
const handleOpen = (() => {
  emit('open')
});

/** Dialog 打开动画结束时的回调 */
const handleOpened = (() => {
  bus.on(KEY_RESIZE, onEmitResize);
  emit('opened')
});

/** Dialog 关闭的回调 */
const handleClose = (() => {
  emit('close')
});

/** Dialog 关闭动画结束时的回调 */
const handleClosed = (done: () => void) => {
  bus.off(KEY_RESIZE);
  emit('closed')
  fullscreen.value = false
};

// 计算弹窗body最大高度
const maxHeight = computed(() => {
  return fullscreen.value || !props.autoHeight || props.drawer ? '100%' : '55vh';
});

// 计算弹窗body占用高度
const computeDialogHeight = () => {
  // 如果当前处于全屏
  if (fullscreen.value || props.drawer) {
    const windowHeight = document.documentElement.offsetHeight
    dialogHeight.value = `${windowHeight - 61 - 32 - (slots.footer ? 57 : 0)}px`
  } else {
    // 默认设置的值
    dialogHeight.value = isNumber(props.bodyHeight) ? `${props.bodyHeight}px` : props.bodyHeight
  }
};

watch(() => fullscreen.value,
  async (val: boolean) => {
    await nextTick()
    computeDialogHeight();
  },
  {immediate: true}
);

const dialogStyle = computed(() => {
  if (fullscreen.value || !props.autoHeight) {
    return {height: unref(dialogHeight)};
  }
  return {minHeight: '150px'};
});

</script>

<style lang="scss" scoped>
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 32px;
  position: relative;
  font-size: 15px;
}

:deep(.el-dialog__title) {
  font-size: 15px;
  font-weight: 500;
}

.operate {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 36px;

  .svg-icon {
    color: var(--el-color-info);
    font-size: inherit;

    &:hover {
      color: var(--el-color-primary)
    }
  }
}
</style>
